package cn.vc.content.service.video;

import cn.vc.basicmodule.entity.Result;
import cn.vc.content.entity.video.dto.VideoTap;
import cn.vc.content.mapper.video.VideoMapper;
import cn.vc.media.entity.po.MediaFiles;
import cn.vc.media.feign.clients.MediaFilesClient;
import cn.vc.content.entity.video.dto.VideoDto;
import cn.vc.content.entity.video.po.Video;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
//import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class VideoService {
    private final VideoMapper videoMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    /**
     * 媒资文件前缀
     * {KEY}{文件ID}
     */
    private static final String MEDIA_FILES_KEY = "mediafiles:";
    private final HttpServletRequest request;
    @Autowired
    private MediaFilesClient mediaFilesClient;
    /**
     * 拥有点击计数器缓存的视频ID
     */
    private static final List<Long> hasTapCacheIdList = new ArrayList<>();

    @Autowired
    public VideoService(VideoMapper videoMapper, HttpServletRequest request) {
        this.videoMapper = videoMapper;
        this.request = request;
    }


    /**
     * 封装Video为VideoDto
     */
    protected List<VideoDto> packingVideoList(List<Video> videoList) {
        // 获取视频流的id数组
        List<Long> ids = new ArrayList<>();
        videoList.forEach(video -> {
            ids.add(video.getMediaId());
        });
        // 通过id数组查询对应的文件信息
        List<MediaFiles> mediaFilesList = mediaFilesClient.selectByIds(ids);
        // 封装视频信息dto
        List<VideoDto> videoDtoList = new ArrayList<>();
        for (int i = 0; i < videoList.size(); i++) {
            VideoDto videoDto = new VideoDto();
            // 拷贝相同字段
            BeanUtils.copyProperties(videoList.get(i), videoDto);
            // 拿到对应所有的视频文件信息
            MediaFiles mediaFiles = mediaFilesList.get(i);
            // 手动插入视频文件的信息
            // 插入视频地址
            videoDto.setVideoUrl(mediaFiles.getUrl());
            // 插入数组中
            videoDtoList.add(videoDto);
        }
        return videoDtoList;
    }


    /**
     * <h2>后台服务申请通过后进行发布</h2>
//     * @param id 需要发布的视频ID
     * @return
     */
//    @RabbitListener(queues = "video.queue.publish")
//    public void publish(String id) {
//        User myUser = redisTemplate.opsForValue().get(request.getHeader("token"))!=null? JSON.parseObject(redisTemplate.opsForValue().get(request.getHeader("token")).toString(), User.class):null;
//        if (myUser==null) return;
//        String json = redisTemplate.opsForValue().get("video:"+myUser.getId()+":"+id);
//        if (json==null) return;
//        Video video = JSON.parseObject(json, Video.class);
//        video.setId(null);
//        boolean bo = videoMapper.insert(video)==1;
//        log.info("申请通过 -> "+id+", "+bo);
//    }
    public boolean add(Video video) {
        if (video==null) return false;
        video.setId(null);
        return videoMapper.insert(video)==1;
    }
    /**
     * 审核通过
     */
    public int passTheAudit(Long id) {
        Video video = videoMapper.selectById(id);
        video.setIsDraft(false);
        return videoMapper.updateById(video);
    }
    /**
     * 添加点击数
     * @param videoId
     */
    public void tap(Long videoId) {
        BoundValueOperations<String, String> valueOps = stringRedisTemplate.boundValueOps("video:"+videoId+":tap");
        // 缓存存在时点击数加一
        if (valueOps.get()!=null) {
            valueOps.increment();
        }
//        Video video = videoMapper.selectById(videoId);
//        video.setTapNum(video.getTapNum()+1);
//        videoMapper.updateById(video);

//        List<VideoTap> videoTapList;
//        String videoTapListJSON = stringRedisTemplate.opsForValue().get("video_tap_list");
//        if (videoTapListJSON==null) {
//            videoTapList = new ArrayList<>();
//        } else {
//            videoTapList = JSON.parseArray(videoTapListJSON, VideoTap.class);
//        }
//        videoTapList.add(new VideoTap(videoId, 1L));
//        stringRedisTemplate.opsForValue().set("video_tap_list", JSON.toJSONString(videoTapList));
    }
    /**
     * 定时添加视频点击数
     */
    @XxlJob("videoTapJobHandler")
    public void videoTapJobHandler() {
        // 需要清楚的ID
        List<Long> oldIds = new ArrayList<>();
        hasTapCacheIdList.forEach(id -> {
            BoundValueOperations<String, String> valueOps = stringRedisTemplate.boundValueOps("video:"+id+":tap");
            String num = valueOps.get();
            if (num==null) {
                oldIds.add(id);
            } else {
                // 修改点击数
                LambdaUpdateWrapper<Video> luw = new LambdaUpdateWrapper<>();
                luw.eq(Video::getId, id);
                luw.set(Video::getTapNum, Long.parseLong(num));
                videoMapper.update(null, luw);
            }
        });
        // 删除空缓存ID
        oldIds.forEach(hasTapCacheIdList::remove);
    }
    /**
     * 点赞
     * @param videoId
     */
    public void like(Long videoId) {
        Video video = videoMapper.selectById(videoId);
        video.setLikeNum(video.getLikeNum()+1);
        videoMapper.updateById(video);
    }
    /**
     * 取赞
     * @param videoId
     */
    public void noLike(Long videoId) {
        Video video = videoMapper.selectById(videoId);
        video.setLikeNum(video.getLikeNum()<=0?0:video.getLikeNum()-1);
        videoMapper.updateById(video);
    }
    /**
     * 投币
     * @param videoId
     */
    public void coins(Long videoId, int coins) {
        Video video = videoMapper.selectById(videoId);
        video.setCoinsNum(video.getCoinsNum()+coins);
        videoMapper.updateById(video);
    }
    /**
     * 收藏
     * @param videoId
     */
    public void collect(Long videoId) {
        Video video = videoMapper.selectById(videoId);
        video.setLikeNum(video.getCollectNum()+1);
        videoMapper.updateById(video);
    }
    /**
     * 取消收藏
     * @param videoId
     */
    public void noCollect(Long videoId) {
        Video video = videoMapper.selectById(videoId);
        video.setLikeNum(video.getCollectNum()<=0?0:video.getCollectNum()-1);
        videoMapper.updateById(video);
    }
    /**
     *  对指定ID视频评论
     * @param videoId
     */
    public void review(Long videoId) {
        Video video = videoMapper.selectById(videoId);
        video.setReviewNum(video.getReviewNum()+1);
        videoMapper.updateById(video);
    }



    /**
     * 查询当前插入数据ID
     * @return
     */
    public Long selectCurrentId() {
        return videoMapper.selectCurrentId();
    }
    /**
     * 查询需要审核的视频
     * @return
     */
    public List<VideoDto> selectAllByIsDraft() {
        // 查询所有需要审核的视频信息
        LambdaQueryWrapper<Video> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Video::getIsDraft, true);
        // 分页查询，只查询前十条
        Page<Video> videoPage = new Page<>(1, 10);
        IPage<Video> videoIPage = videoMapper.selectPage(videoPage, lqw);
        List<Video> videoList = videoIPage.getRecords();
        return this.packingVideoList(videoList);
    }
    /**
     * 查询审核通过的视频
     * @return
     */
    public List<VideoDto> selectAllByNotDraft() {
        List<VideoDto> videoDtoList = JSON.parseArray(stringRedisTemplate.opsForValue().get("video/all_by_notdraft"), VideoDto.class);
        // 没有则去数据库查询
        if (videoDtoList==null) {
            // 查询所有视频信息
            LambdaQueryWrapper<Video> lqw = new LambdaQueryWrapper<>();
            lqw.eq(Video::getIsDraft, false);
            List<Video> videoList = videoMapper.selectList(lqw);
            // 获取视频流的id数组
            List<Long> ids = new ArrayList<>();
            videoList.forEach(video -> {
                ids.add(video.getMediaId());
            });
            // 通过id数组查询对应的文件信息
            List<MediaFiles> mediaFilesList = mediaFilesClient.selectByIds(ids);
            // 封装视频信息dto
            videoDtoList = new ArrayList<>();
            for (int i = 0; i < videoList.size(); i++) {
                VideoDto videoDto = new VideoDto();
                // 拷贝相同字段
                BeanUtils.copyProperties(videoList.get(i), videoDto);
                // 拿到对应所有的视频文件信息
                MediaFiles mediaFiles = mediaFilesList.get(i);
                // 手动插入视频文件的信息
                // 插入视频地址
                videoDto.setVideoUrl(mediaFiles.getUrl());
                // 插入数组中
                videoDtoList.add(videoDto);
            }
            stringRedisTemplate.opsForValue().set("video/all_by_notdraft", JSON.toJSONString(videoDtoList), 30, TimeUnit.SECONDS);
        }
        return videoDtoList;
    }
    public VideoDto selectById(Long id) {
        VideoDto videoDto = JSON.parseObject(stringRedisTemplate.opsForValue().get("videodto:"+id), VideoDto.class);
        // 没有则去数据库查询
        if (videoDto==null) {
            // System.out.println("已过期");
            // 查询视频信息
            Video video = videoMapper.selectById(id);
            if (video==null) return null;
            // 封装视频信息dto
            videoDto = new VideoDto();
            BeanUtils.copyProperties(video, videoDto);
            // 查询视频文件信息
            Result<MediaFiles> result = mediaFilesClient.selectById(video.getMediaId());
            if (result.getCode()==0) {
                return null;
            }
            // 插入视频地址
            videoDto.setVideoUrl(result.getData().getUrl());
            stringRedisTemplate.opsForValue().set("videodto:"+id.toString(), JSON.toJSONString(videoDto), 30, TimeUnit.SECONDS);
            BoundValueOperations<String, String> valueOps = stringRedisTemplate.boundValueOps("video:"+videoDto.getId()+":tap");
            // 若点击计数器值为空，说明没有缓存，加入基础点击数
            if (valueOps.get()==null) {
                valueOps.increment(videoDto.getTapNum());
            }
            // 若需要更新点击数的视频ID中不存在，则添加
            if (!hasTapCacheIdList.contains(videoDto.getId())) hasTapCacheIdList.add(videoDto.getId());
        }
        return videoDto;
    }
    /**
     * 查询分页
     * @param current 当前页
     * @param size 每页显示条数
     */
    public List<VideoDto> selectPage(Long current, Long size) {
        // 设置查询条件
        Page<Video> page = new Page<>(current, size, false);
        LambdaQueryWrapper<Video> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Video::getIsDraft,false);
        // 查询
        IPage<Video> ipage = videoMapper.selectPage(page, lqw);
        // 封装为videoDto
        List<VideoDto> videoDtoList = ipage.getRecords().stream()
                .map(this::getVideoDto)
                .collect(Collectors.toList());
        return videoDtoList;
    }
    public List<VideoDto> selectByIds(List<Long> ids) {
        List<VideoDto> videoDtoList = new ArrayList<>();
        ids.forEach(id -> {
            videoDtoList.add(this.selectById(id));
        });
        return videoDtoList;
    }
    public List<VideoDto> selectByUserId(Long userId) {
        LambdaQueryWrapper<Video> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Video::getUserId, userId);
        List<Video> videoList = videoMapper.selectList(lqw);
        return this.packingVideoList(videoList);
    }


    /**
     * 封装video为videodto
     * @param video
     * @return
     */
    protected VideoDto getVideoDto(Video video) {
        if (video==null) return null;
        // 封装视频信息dto
        VideoDto videoDto = new VideoDto();
        BeanUtils.copyProperties(video, videoDto);
        MediaFiles mediaFiles = JSON.parseObject(stringRedisTemplate.opsForValue().get(MEDIA_FILES_KEY+video.getMediaId()), MediaFiles.class);
        // 缓存中不存在，去查询
        if (mediaFiles==null) {
            // 查询视频文件信息
            Result<MediaFiles> result = mediaFilesClient.selectById(video.getMediaId());
            if (result.getCode()==0) {
                return null;
            }
            mediaFiles = result.getData();
            // 存入缓存
            stringRedisTemplate.opsForValue().set(MEDIA_FILES_KEY+video.getMediaId(), JSON.toJSONString(mediaFiles), 30, TimeUnit.SECONDS);
        }
        // 插入视频地址
        videoDto.setVideoUrl(mediaFiles.getUrl());
        return videoDto;
    }
}
